package com.nce.converter.pcap;

import java.io.IOException;
import java.util.HashMap;

import javax.media.format.AudioFormat;

import jpcap.JpcapCaptor;
import jpcap.packet.Packet;
import jpcap.packet.UDPPacket;

import com.nce.converter.Format;
import com.nce.protocol.rtp.NotAValidHeaderException;
import com.nce.protocol.rtp.RTPHeader;

public class Preprocessor implements com.nce.converter.Preprocessor {
	
	private long initialCapturedTimeSec = -1;
	private long initialCapturedTimeUSec = -1;
	private long frequency = -1;
	private double sampleMicroPeriod = -1;
	private final static long SEC_TO_MICRO_SCALE = 1000000; /*a million, 6 zeroes*/
	
	private int nStreams = 0;
	
	private JpcapCaptor captor = null;
	
	private HashMap<String, Object> infoMap = null;
	
	private HashMap<Integer, Long> participantsList = null;
	
	private boolean inited = false;
	private boolean preprocessed = false;
	private boolean errorEncountered = false;
	
	
	
	public HashMap<String, Object> getInfo() {
		if (preprocessed && !errorEncountered)
		{
			return infoMap;
		}
		return null;
	}

	public boolean init(Format format, String fileName) {
		inited = false;
		
		try {
			captor = JpcapCaptor.openFile(fileName);
			infoMap = new HashMap<String, Object>();
			infoMap.put("input.file.format", format);
			infoMap.put("input.file.name", fileName);
			participantsList = new HashMap<Integer, Long>();
			inited = true;
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			inited = false;
		}
		
		return inited;
	}

	public boolean preprocess() {
		if (!inited)
			return false;
		if (preprocessed)
			return true;
		while(true){
			  //read a packet from the opened file
			  Packet packet=captor.getPacket();
			  //if some error occurred or EOF has reached, break the loop
			  if(packet==null || packet==Packet.EOF) break;
			  //otherwise, print out the packet
			  if (packet instanceof UDPPacket)
			  {
				try {
					RTPHeader header = new RTPHeader(packet.data);
					if (!isParticipantListed(header.ssrc))
					{
						if (frequency == -1)
						{
							updateSampleFrequency(header);
						}
						newParticipant(header.ssrc, packet);
					}
					
				} 
				catch (NotAValidHeaderException ex)
				{
					System.err.println("error in sending the data to the stream: \n" + ex);
					errorEncountered = true;
					break;
				}
			  }
			}
		captor.close();
		infoMap.put("input.ssrc_and_offset", participantsList);
		infoMap.put("input.streams_number", nStreams);
		if (!errorEncountered)
		{
			preprocessed = true;
		}
		return preprocessed;
	}
	
	private void newParticipant(int ssrc, Packet p){
		long offset = getOffset(p);
		participantsList.put(ssrc, offset);
		nStreams++;
	}
	
	private void updateSampleFrequency(RTPHeader header)
	{
		String encoding = null;
		switch (header.payloadType)
		{
			case 0 : /* G.711 U-Law, 8Khz*/
			default :
				encoding = AudioFormat.ULAW;
				frequency = 8000;
				break;
		} 
		sampleMicroPeriod = (SEC_TO_MICRO_SCALE)/frequency;
		infoMap.put("input.audio.encoding", encoding);
		infoMap.put("input.audio.samplerate", frequency);
	}
	
	private long getOffset(Packet pack)
	{
		if (initialCapturedTimeSec == -1)
		{
			initialCapturedTimeSec = pack.sec;
			initialCapturedTimeUSec = pack.usec;
			return 0;
		}
		long deltaSec = pack.sec - initialCapturedTimeSec;
		long deltaUSec = -1;
		if (pack.usec > initialCapturedTimeUSec)
		{
			deltaUSec = pack.usec - initialCapturedTimeUSec;
		} else
		{
			deltaUSec = initialCapturedTimeUSec - pack.usec;
			deltaSec--;
		}
		long offsetInSamples = Math.round(((deltaSec * SEC_TO_MICRO_SCALE) + deltaUSec) / sampleMicroPeriod); 
		return offsetInSamples;
	}
	
	private boolean isParticipantListed(int ssrc)
	{
		return participantsList.containsKey(ssrc);
	}
}
